
package cfar.actors;


import cfar.actors.Communication.Recievable;
import cfar.actors.Communication.Tellable;
import cfar.actors.distributed.Signal;
import cfar.ff.data.Unit;
import cfar.utils.IO;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author badi
 */
public class Node implements Tellable, Recievable {

    final Location server_location;
    Socket worker;
    final protected UUID id;

    protected final InetAddress address;
    protected final int port;

    public Node(final InetAddress address, final int port) throws IOException {
        server_location = new Location(address, port);
        id = UUID.randomUUID();

        this.address = address;
        this.port = port;
    }

    public Node copy(){
        try {
            return new Node(server_location.$1, server_location.$2);
        } catch (IOException ex) {
            Logger.getLogger(Node.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    Socket connect_to_worker() throws IOException, ClassNotFoundException{
        Socket server = new Socket(server_location.$1, server_location.$2);
        IO.write_object(new Message(Signal.Spawn, Unit.unit()), server.getOutputStream());
        Location loc = ((Message<Location>) IO.read_object(server.getInputStream())).payload;
        IO.write_object(new Message(Signal.Close, Unit.unit()), server.getOutputStream());
        server.close();
        return new Socket(loc.$1, loc.$2);
    }

    public void tell(Message msg) {
        try {
            if(msg.signal instanceof Signal.Stop){ // || msg.signal instanceof Signal.Close){
                Socket server = new Socket(server_location.$1, server_location.$2);
                IO.write_object(msg, server.getOutputStream());
                server.close();
            }
            worker = worker == null
                    ? connect_to_worker()
                    : worker;
            IO.write_object(msg, worker.getOutputStream());
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Node.class.getName()).log(Level.SEVERE, msg.toString(), ex);
        } catch (IOException ex) {
            Logger.getLogger(Node.class.getName()).log(Level.SEVERE, msg.toString(), ex);
        }
    }

    public Message call() {
        Message m = null;
        try {
             m = (Message) IO.read_object(worker.getInputStream());
        } catch (IOException ex) {
            Logger.getLogger(Node.class.getName()).log(Level.SEVERE, m + "", ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Node.class.getName()).log(Level.SEVERE, m + "", ex);
        }
        return m;
    }

    public Location server_location(){
        return server_location;
    }
}
